package co.baiku.boot.weixin;

import co.baiku.boot.common.spring.SpringContext;
import co.baiku.boot.common.tools.Base64Tools;
import co.baiku.boot.common.tools.JsonTools;
import co.baiku.boot.common.tools.ServletTools;
import co.baiku.boot.common.tools.StringTools;
import co.baiku.boot.common.validate.Validation;
import co.baiku.boot.config.WeixinPayV3Config;
import co.baiku.boot.pay.v3.callback.*;
import co.baiku.boot.pay.v3.httpclient.WechatPayHttpClientBuilder;
import co.baiku.boot.pay.v3.httpclient.util.PemUtil;
import co.baiku.boot.v3.callback.WeixinPayScoreConfirmOrderCallback;
import co.baiku.boot.v3.model.WeixinPayScoreCallbackResource;
import co.baiku.boot.v3.model.WeixinPayScoreRequest;
import co.baiku.boot.v3.model.WeixinPayScoreUserAuthorizeState;
import co.baiku.boot.pay.v3.response.*;
import co.baiku.boot.v3.request.*;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpRequest;
import org.apache.http.NoHttpResponseException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.SSLException;
import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.UnknownHostException;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;

@Component
@ConditionalOnProperty(value = "ajavaer.weixin.pay.v3.appid")
@EnableConfigurationProperties({WeixinPayV3Config.class})
public class WeixinPayV3 implements InitializingBean, ApplicationListener<ContextClosedEvent> {
    private Logger log = LoggerFactory.getLogger(WeixinPayV3.class);
    private final String BASE_URL = "https://api.mch.weixin.qq.com";
    private final String SERVER_ORDER = "/v3/payscore/serviceorder";
    private CloseableHttpClient httpClient;
    private boolean debug = false;

    @Resource
    private WeixinPayV3Config weixinPayV3Config;

    /**
     * 证书下载接口--支持首次调用
     *
     * @return
     */
    public WeixinPayV3Response<WeixinPayV3CertificatesResponse> certificate() {
        return doGetRequest(BASE_URL + "/v3/certificates", WeixinPayV3CertificatesResponse.class);
    }

    /**
     * 创建支付分订单API<br/>
     * 用户申请使用服务时，商户可通过此接口申请创建微信支付分订单。
     *
     * @param request WeixinPayScoreCreateOrderRequest
     * @return WeixinPayV3Response
     * @see <a href='https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore/chapter3_1.shtml'>官方文档</a>
     */
    public WeixinPayV3Response<WeixinPayScoreOrderResponse> payScoreCreateOrder(WeixinPayScoreCreateOrderRequest request) {
        return doPostRequest(BASE_URL + SERVER_ORDER, request, WeixinPayScoreOrderResponse.class);
    }

    /**
     * 查询支付分订单API<br/>
     * 用于查询单笔微信支付分订单详细信息。
     *
     * @param request WeixinPayScoreQueryOrderRequest
     * @return WeixinPayV3Response
     * @see <a href='https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore/chapter3_2.shtml'>官方文档</a>
     */
    public WeixinPayV3Response<WeixinPayScoreOrderResponse> payScoreQueryOrder(WeixinPayScoreQueryOrderRequest request) {
        return doGetRequest(BASE_URL + SERVER_ORDER + request.requestString(), WeixinPayScoreOrderResponse.class);
    }

    /**
     * 取消支付分订单API
     * <pre>
     * 前置条件：服务订单状态为已创单或进行中，且从未成功使用完结订单能力
     * 服务从未实际发生，且确认用户不使用服务，商户可使用此能力取消订单。
     * 注意：
     * • 本能力仅在“服务从未实际发生”时使用。若服务已发生，但不需要向用户收钱（例如优惠金额已经抵扣了所有的付费项目金额），请使用完结订单能力0元结单。
     * </pre>
     *
     * @param request WeixinPayScoreCancelOrderRequest
     * @return WeixinPayV3Response
     * @see <a href='https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore/chapter3_3.shtml'>官方文档</a>
     */
    public WeixinPayV3Response<WeixinPayScoreCancelOrderResponse> cancel(WeixinPayScoreCancelOrderRequest request) {
        String url = StringTools.format("/{0}/cancel", request.getOutOrderNo());
        request.setOutOrderNo(null);
        return doPostRequest(BASE_URL + SERVER_ORDER + url, request, WeixinPayScoreCancelOrderResponse.class);
    }

    /**
     * 修改订单金额API
     * <pre>
     * 前置条件：服务订单支付状态为待支付
     * 限制条件：同一笔订单仅能修改1次
     * 完结订单总金额与实际金额不符时，可通过该接口修改订单金额。
     * 例如：充电宝场景，由于机器计费问题导致商户完结订单时扣除用户99元，用户客诉成功后，商户需要按照实际的消费金额（如10元）扣费，当服务订单支付状态处于“待支付”时，商户可使用此能力修改订单金额。
     *
     * 注意：
     * • 若此笔订单已收款成功，商户直接使用退款能力，将差价退回用户即可。
     * </pre>
     *
     * @param request WeixinPayScoreModifyOrderRequest
     * @return WeixinPayV3Response
     * @see <a href='https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore/chapter3_4.shtml'>官方文档</a>
     */
    public WeixinPayV3Response<WeixinPayScoreOrderResponse> payScoreModifyOrder(WeixinPayScoreModifyOrderRequest request) {
        String url = StringTools.format("/{0}/modify", request.getOutOrderNo());
        request.setOutOrderNo(null);
        return doGetRequest(BASE_URL + SERVER_ORDER + url, WeixinPayScoreOrderResponse.class);
    }

    /**
     * 完结支付分订单API
     * <pre>
     * 完结微信支付分订单。用户使用服务完成后，商户可通过此接口完结订单。
     * 前置条件：用户确认订单成功后
     * </pre>
     *
     * @param request WeixinPayScoreCompleteOrderRequest
     * @return WeixinPayV3Response
     * @see <a href='https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore/chapter3_5.shtml'>官方文档</a>
     */
    public WeixinPayV3Response<WeixinPayScoreOrderResponse> payScoreCompleteOrder(WeixinPayScoreCompleteOrderRequest request) {
        String url = StringTools.format("/{0}/complete", request.getOutOrderNo());
        request.setOutOrderNo(null);
        return doPostRequest(BASE_URL + SERVER_ORDER + url, request, WeixinPayScoreOrderResponse.class);
    }

    /**
     * 支付分订单收款API
     * <pre>
     * 当微信支付分订单支付状态处于“待支付”时，商户可使用该接口向用户发起收款。
     * 注意：
     * • 此能力不影响微信支付分代商户向用户发起收款的策略。
     * </pre>
     *
     * @param request WeixinPayScoreRequest
     * @return WeixinPayV3Response
     * @see <a href='https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore/chapter3_6.shtml'>官方文档</a>
     */
    public WeixinPayV3Response<WeixinPayScorePayOrderResponse> payScoreCompleteOrder(WeixinPayScoreRequest request) {
        String url = StringTools.format("/{0}/pay", request.getOutOrderNo());
        request.setOutOrderNo(null);
        return doPostRequest(BASE_URL + SERVER_ORDER + url,request, WeixinPayScorePayOrderResponse.class);
    }

    /**
     * 同步服务订单信息API
     * <pre>
     * 前提条件：同步商户渠道收款成功信息时，即场景类型=“Order_Paid”，订单的状态需为[FINISHED:商户完结订单]
     * 由于收款商户进行的某些“线下操作”会导致微信支付侧的订单状态与实际情况不符。
     * 例如，用户通过线下付款的方式已经完成支付，而微信支付侧并未支付成功，此时可能导致用户重复支付。因此商户需要通过订单同步接口将订单状态同步给微信支付，修改订单在微信支付系统中的状态。
     * </pre>
     *
     * @param request WeixinPayScoreSyncRequest
     * @return WeixinPayV3Response
     * @see <a href='https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore/chapter3_7.shtml'>官方文档</a>
     */
    public WeixinPayV3Response<WeixinPayScoreOrderResponse> payScoreSyncOrder(WeixinPayScoreSyncOrderRequest request) {
        String url = StringTools.format("/{0}/sync", request.getOutOrderNo());
        request.setOutOrderNo(null);
        return doGetRequest(BASE_URL + SERVER_ORDER + url, WeixinPayScoreOrderResponse.class);
    }

    /**
     * 发送get请求
     *
     * @param url        请求地址
     * @param returnType 返回类型
     * @param <T>        目标类型
     * @return WeixinPayV3Response
     */
    public <T> WeixinPayV3Response<T> doGetRequest(String url, Class<T> returnType) {
        return request(HttpMethod.GET, url, null, returnType);
    }

    /**
     * 发送post请求
     *
     * @param url        请求地址
     * @param request    requestBody数据
     * @param returnType 返回类型
     * @param <T>        目标类型
     * @return WeixinPayV3Response
     */
    public <T> WeixinPayV3Response<T> doPostRequest(String url, WeixinPayScoreRequest request, Class<T> returnType) {
        if (request.getAppid() == null) {
            request.setAppid(weixinPayV3Config.getAppid());
        }
        if (request.getServiceId() == null) {
            request.setServiceId(weixinPayV3Config.getServiceId());
        }
        return request(HttpMethod.POST, url, JsonTools.beanToJson(request), returnType);
    }

    /**
     * 确认订单回调通知
     * <pre>
     * 微信支付分通过确认订单通知接口将用户确认订单消息通知给商户
     * 注意：
     * • 同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。 推荐的做法是，当商户系统收到通知进行处理时，先检查对应业务数据的状态，并判断该通知是否已经处理。如果未处理，则再进行处理；如果已处理，则直接返回结果成功。在对业务数据进行状态检查和处理之前，要采用数据锁进行并发控制，以避免函数重入造成的数据混乱。
     * • 如果在所有通知频率(4小时)后没有收到微信侧回调,商户应调用查询订单接口确认订单状态。
     * 特别提醒：商户系统对于确认订单通知的内容一定要做签名验证，并校验通知的信息是否与商户侧的信息一致，防止数据泄漏导致出现“假通知”，造成资金损失。
     * </pre>
     *
     * @param request          request
     * @param callbackConsumer Consumer
     * @return boolean
     */
    public boolean payScoreConfirmOrderCallback(HttpServletRequest request, Consumer<WeixinPayScoreConfirmOrderCallback> callbackConsumer) {
        try {
            String requestBody = ServletTools.getRequestBody(request);
            if (debug) {
                log.info("[WeixinPayApiV3 - payScoreConfirmOrderCallback] data:{}", requestBody);
            }
            WeixinPayScoreConfirmOrderCallback callback = JsonTools.jsonToBean(requestBody, WeixinPayScoreConfirmOrderCallback.class);
            WeixinPayScoreCallbackResource resource = callback.getResource();
            String decrypt = resourceDecrypt(resource.getAssociatedData(), resource.getNonce(), resource.getCiphertext());
            resource.setData(JsonTools.jsonToBean(decrypt, WeixinPayScoreOrderResponse.class));
            callbackConsumer.accept(callback);
            return true;
        } catch (Exception e) {
            log.error("[WeixinPayApiV3 - payScoreConfirmOrderCallback] exception:" + e.getMessage(), e);
        }
        return false;
    }

    /**
     * 支付成功回调通知
     * <pre>
     * 微信支付分通过确认订单通知接口将用户确认订单消息通知给商户
     * 注意：
     * • 同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。 推荐的做法是，当商户系统收到通知进行处理时，先检查对应业务数据的状态，并判断该通知是否已经处理。如果未处理，则再进行处理；如果已处理，则直接返回结果成功。在对业务数据进行状态检查和处理之前，要采用数据锁进行并发控制，以避免函数重入造成的数据混乱。
     * • 如果在所有通知频率(4小时)后没有收到微信侧回调,商户应调用查询订单接口确认订单状态。
     * 特别提醒：商户系统对于确认订单通知的内容一定要做签名验证，并校验通知的信息是否与商户侧的信息一致，防止数据泄漏导致出现“假通知”，造成资金损失。
     * </pre>
     *
     * @param request          request
     * @param callbackConsumer Consumer
     * @return boolean
     */
    public boolean payScorePaidOrderCallback(HttpServletRequest request, Consumer<WeixinPayScorePaidOrderCallback> callbackConsumer) {
        try {
            String requestBody = ServletTools.getRequestBody(request);
            if (debug) {
                log.info("[WeixinPayApiV3 - payScorePaidOrderCallback] data:{}", requestBody);
            }
            WeixinPayScorePaidOrderCallback callback = JsonTools.jsonToBean(requestBody, WeixinPayScorePaidOrderCallback.class);
            WeixinPayScoreCallbackResource resource = callback.getResource();
            String decrypt = resourceDecrypt(resource.getAssociatedData(), resource.getNonce(), resource.getCiphertext());
            resource.setData(JsonTools.jsonToBean(decrypt, WeixinPayScoreOrderResponse.class));
            callbackConsumer.accept(callback);
            return true;
        } catch (Exception e) {
            log.error("[WeixinPayApiV3 - payScorePaidOrderCallback] exception:" + e.getMessage(), e);
        }
        return false;
    }

    /**
     * 支付分回调通知(综合回调处理)
     *
     * @param requestBody     请求内容
     * @param callbackHandler 处理句柄
     * @return
     */
    public boolean payScoreCallBack(String requestBody, WeixinPayScoreCallbackHandler callbackHandler) {
        try {
            WeixinPayScoreCallback callback = JsonTools.jsonToBean(requestBody, WeixinPayScoreCallback.class);
            String eventType = callback.getEventType();
            WeixinPayScoreCallbackResource resource = callback.getResource();
            String decrypt = resourceDecrypt(resource.getAssociatedData(), resource.getNonce(), resource.getCiphertext());
            switch (eventType) {
                //授权成功通知
                case "PAYSCORE.USER_OPEN_SERVICE":
                    resource.setData(JsonTools.jsonToBean(decrypt, WeixinPayScoreUserAuthorizeState.class));
                    return callbackHandler.onUserOpenService(callback);
                //解除授权成功通知
                case "PAYSCORE.USER_CLOSE_SERVICE":
                    resource.setData(JsonTools.jsonToBean(decrypt, WeixinPayScoreUserAuthorizeState.class));
                    return callbackHandler.onUserCloseService(callback);
                //用户确认成功通知
                case "PAYSCORE.USER_CONFIRM":
                    resource.setData(JsonTools.jsonToBean(decrypt, WeixinPayScoreOrderResponse.class));
                    return callbackHandler.onUserConfirm(callback);
                //支付成功通知
                case "PAYSCORE.USER_PAID":
                    resource.setData(JsonTools.jsonToBean(decrypt, WeixinPayScoreOrderResponse.class));
                    return callbackHandler.onUserPaid(callback);
            }
        } catch (Exception e) {
            log.error("[WeixinPayApiV3 - payScoreCallBack] exception:" + e.getMessage(), e);
        }
        return false;
    }

    //start****************************免确认特有API*************************/

    /**
     * [免确认特有API] 查询用户授权状态API
     * <pre>
     * 用户申请使用服务时，商户可通过此接口查询用户是否“已授权”本服务。在“已授权”状态下的服务，用户才可以申请使用。
     * </pre>
     *
     * @param openid
     * @return
     * @see <a href='https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore/chapter3_8.shtml'>官方文档</a>
     */
    public WeixinPayV3Response<WeixinPayScoreUserServiceStateResponse> payScoreUserServiceState(String openid) {
        String url = BASE_URL + StringTools.format("/v3/payscore/user-service-state?service_id={0}&appid={1}&openid={2}", weixinPayV3Config.getServiceId(), weixinPayV3Config.getAppid(), openid);
        return doGetRequest(url, WeixinPayScoreUserServiceStateResponse.class);
    }

    /**
     * 授权/解除授权服务回调通知
     * <pre>
     * 微信支付分通过授权/解除授权服务通知接口将用户过授权/解除授权服务消息通知给商户
     * 注意：
     * • 同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。 推荐的做法是，当商户系统收到通知进行处理时，先检查对应业务数据的状态，并判断该通知是否已经处理。如果未处理，则再进行处理；如果已处理，则直接返回结果成功。在对业务数据进行状态检查和处理之前，要采用数据锁进行并发控制，以避免函数重入造成的数据混乱。
     * • 如果在所有通知频率(4小时)后没有收到微信侧回调,商户应调用查询订单接口确认订单状态。
     * 特别提醒：商户系统对于确认订单通知的内容一定要做签名验证，并校验通知的信息是否与商户侧的信息一致，防止数据泄漏导致出现“假通知”，造成资金损失。
     * </pre>
     *
     * @param request          request
     * @param callbackConsumer Consumer
     * @return boolean
     */
    public boolean payScoreUserAuthorizeStateCallback(HttpServletRequest request, Consumer<WeixinPayScoreUserAuthorizeStateCallback> callbackConsumer) {
        try {
            String requestBody = ServletTools.getRequestBody(request);
            if (debug) {
                log.info("[WeixinPayApiV3 - payScoreUserAuthorizeStateCallback] data:{}", requestBody);
            }
            WeixinPayScoreUserAuthorizeStateCallback callback = JsonTools.jsonToBean(requestBody, WeixinPayScoreUserAuthorizeStateCallback.class);
            WeixinPayScoreCallbackResource resource = callback.getResource();
            String decrypt = resourceDecrypt(resource.getAssociatedData(), resource.getNonce(), resource.getCiphertext());
            resource.setData(JsonTools.jsonToBean(decrypt, WeixinPayScoreUserAuthorizeState.class));
            callbackConsumer.accept(callback);
            return true;
        } catch (Exception e) {
            log.error("[WeixinPayApiV3 - payScoreUserAuthorizeStateCallback] exception:" + e.getMessage(), e);
        }
        return false;
    }

    /**
     * [免确认特有API] 商户解除用户授权关系API
     * <pre>
     * 商户通过调用该接口，可主动与用户解除此前授权关系。
     * </pre>
     *
     * @param openid
     * @return
     * @see <a href='https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore/chapter9_1.shtml'>官方文档</a>
     */
    public WeixinPayV3Response payScoreUserPermissionTerminate(String openid) {
        String url = BASE_URL + StringTools.format("/payscore/users/{0}/permissions/{1}/terminate", openid, weixinPayV3Config.getServiceId());
        return doGetRequest(url, null);
    }
    //end****************************免确认特有API*************************/

    /**
     * 发送实际请求
     *
     * @param method     请求类型
     * @param url        请求地址
     * @param data       requestBody数据
     * @param returnType 返回类型
     * @param <T>        目标类型
     * @return WeixinPayV3Response
     */
    public <T> WeixinPayV3Response<T> request(HttpMethod method, String url, String data, Class<T> returnType) {
        String applicationJson = "application/json";
        CloseableHttpResponse response = null;
        WeixinPayV3Response result = new WeixinPayV3Response();
        try {
            HttpRequestBase requestBase = null;
            if (method == HttpMethod.GET) {
                requestBase = new HttpGet(url);
            } else if (method == HttpMethod.POST) {
                HttpPost httpPost = new HttpPost(url);
                // NOTE: 建议指定charset=utf-8。低于4.4.6版本的HttpCore，不能正确的设置字符集，可能导致签名错误
                if (StringTools.isNotBlank(data)) {
                    StringEntity reqEntity = new StringEntity(data, ContentType.create(applicationJson, StringTools.UTF8));
                    httpPost.setEntity(reqEntity);
                }
                requestBase = httpPost;
            }
            if (requestBase == null) {
                throw Validation.error("不支持" + method + "请求方式");
            }
            requestBase.addHeader("Accept", applicationJson);

            response = httpClient.execute(requestBase);
            HttpEntity entity = response.getEntity();
            String responseString = null;
            if (entity != null) {
                responseString = EntityUtils.toString(entity);
            }
            if (debug) {
                log.info("[WeixinPayApiV3 - request] url:{},method:{},data:{},response:{}", url, StringTools.nvl(data, "null"), method, responseString);
            }
            if (response.getStatusLine().getStatusCode() == 200) {
                T returnData = null;
                if (returnType == Map.class) {
                    returnData = (T) JsonTools.jsonToMap(responseString, false);
                } else if (returnType == String.class) {
                    returnData = (T) responseString;
                } else {
                    returnData = (T) JsonTools.jsonToBean(responseString, returnType);
                }
                result.setData(returnData);
                return result;
            } else if (response.getStatusLine().getStatusCode() == 204) {
                return result;
            }
            return JsonTools.jsonToBean(responseString, WeixinPayV3Response.class);
        } catch (Exception e) {
            log.error("url:" + url + ",method:" + method + ",data:" + data + ",error:" + e.getMessage(), e);
            result.setMessage(e.getMessage());
        } finally {
            if (response != null) {
                try {
                    response.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        result.setCode("EXCEPTION");
        return result;
    }

    public WeixinPayV3() {

    }

    public WeixinPayV3(WeixinPayV3Config weixinPayV3Config) {
        this();
        this.weixinPayV3Config = weixinPayV3Config;
        this.afterPropertiesSet();
    }

    @Override
    public void afterPropertiesSet() {
        List<X509Certificate> listCertificates = new ArrayList<>();
        try {
            //证书初始化
            PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(
                    new ByteArrayInputStream(weixinPayV3Config.getPrivateKey().getBytes(StringTools.UTF8)));
            if (StringTools.isNotBlank(weixinPayV3Config.getCertificate())) {
                X509Certificate wechatpayCertificate = PemUtil.loadCertificate(
                        new ByteArrayInputStream(weixinPayV3Config.getCertificate().getBytes(StringTools.UTF8)));
                listCertificates.add(wechatpayCertificate);
            }
            httpClient = WechatPayHttpClientBuilder.create()
                    .withMerchant(weixinPayV3Config.getMchId(), weixinPayV3Config.getMchSerialNo(), merchantPrivateKey)
                    .withWechatpay(listCertificates)
                    //添加重试规则
                    .setRetryHandler((e, retryTimes, arg2) -> {
                        if (retryTimes > 3) {
                            return false;
                        }
                        if (e instanceof UnknownHostException || e instanceof ConnectTimeoutException
                                || !(e instanceof SSLException) || e instanceof NoHttpResponseException) {
                            return true;
                        }
                        HttpClientContext clientContext = HttpClientContext.adapt(arg2);
                        HttpRequest request = clientContext.getRequest();
                        return !(request instanceof HttpEntityEnclosingRequest);
                    }).build();
        } catch (Exception e) {
            log.error("微信V3支付证书初始化失败:" + e.getMessage(), e);
        }
        Environment environment = SpringContext.getIfPresent(Environment.class);
        if (environment != null) {
            this.debug = Boolean.parseBoolean(environment.getProperty("ajavaer.debug", "false"));
        } else {
            this.debug = true;
        }
    }

    @Override
    public void onApplicationEvent(ContextClosedEvent contextClosedEvent) {
        try {
            httpClient.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public String resourceDecrypt(String associatedData, String nonce, String ciphertext) throws Exception {
        Security.setProperty("crypto.policy", "unlimited");
        final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        SecretKeySpec key = new SecretKeySpec(weixinPayV3Config.getAesKey().getBytes(), "AES");
        GCMParameterSpec spec = new GCMParameterSpec(128, nonce.getBytes());
        cipher.init(Cipher.DECRYPT_MODE, key, spec);
        cipher.updateAAD(associatedData.getBytes());
        return new String(cipher.doFinal(Base64Tools.decode(ciphertext)));
    }

    public WeixinPayV3Config getWeixinPayV3Config() {
        return weixinPayV3Config;
    }
}
